////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxcawbstatsprocessor.cpp
/// @brief The class that implements IStatsProcessor for AWB.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxcawbioutil.h"
#include "camxcawbstatsprocessor.h"
#include "camxhal3module.h" /// @todo (CAMX-1796) Remove all references of HAL3 from core driver
#include "camxmem.h"
#include "camxstatsdebuginternal.h"
#include "camxtrace.h"

CAMX_NAMESPACE_BEGIN

const CHAR* pDefaultAlgorithmAWBLibraryName = "com.qti.stats.awb";
#if defined(_LP64)
const CHAR* pDefaultAlgorithmAWBLibraryPath = "/vendor/lib64/camera/components/";
#else // _LP64
const CHAR* pDefaultAlgorithmAWBLibraryPath = "/vendor/lib/camera/components/";
#endif // _LP64
static const CHAR* pFunctionName            = "CreateAWBAlgorithm";

// Forward declaration of implementation classes
class  TunedParameterManager;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::Create(
    IStatsProcessor** ppAWBStatsProcessor)
{
    CamxResult          result              = CamxResultSuccess;
    CAWBStatsProcessor* pAWBStatsProcessor  = NULL;

    if (NULL != ppAWBStatsProcessor)
    {
        pAWBStatsProcessor = CAMX_NEW CAWBStatsProcessor;
        if (NULL != ppAWBStatsProcessor)
        {
            *ppAWBStatsProcessor = pAWBStatsProcessor;
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupAWB, "AWB stats processor create failed - out of memory");
            result = CamxResultENoMemory;
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupAWB, "AWB stats processor create failed - Invalid arguments");
        result = CamxResultEInvalidArg;
    }

    CAMX_LOG_INFO(CamxLogGroupAWB, "CAWBStatsProcessor::Create result = %d", result);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::CAWBStatsProcessor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAWBStatsProcessor::CAWBStatsProcessor()
    : m_hHandle(NULL)
{
    CAMX_ENTRYEXIT(CamxLogGroupAWB);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::VendorTagListAllocation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::VendorTagListAllocation()
{
    CAMX_ENTRYEXIT(CamxLogGroupAWB);

    CamxResult      result       = CamxResultSuccess;
    AWBAlgoGetParam algoGetParam = {};

    algoGetParam.type = AWBGetParamTypePublishingVendorTagsInfo;

    result = AlgoGetParam(NULL, &algoGetParam);

    if (CamxResultSuccess == result)
    {
        // allocate memory to hold each vendor tag's data
        result = m_pAWBIOUtil->AllocateMemoryVendorTag(&algoGetParam);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::Initialize(
    const StatsInitializeData* pInitializeData)
{
    CAMX_ASSERT(NULL != pInitializeData);

    CamxResult              result          = CamxResultSuccess;
    const StaticSettings*   pStaticSettings = pInitializeData->pHwContext->GetStaticSettings();
    const CHAR*             pLibraryName    = NULL;
    const CHAR*             pLibraryPath    = NULL;
    VOID*                   pFuncCreateAWB  = NULL;

    CAMX_ASSERT(pStaticSettings != NULL);

    AWBAlgoCreateParamList              createParamList                       = { 0 };
    StatsTuningData                     statsTuningData                       = { 0 };
    AWBAlgoIlluminantsCalibrationFactor illuminantsCalibrationFactor          = { 0 };
    AWBAlgoCreateParam                  createParams[AWBCreateParamTypeCount] = {};
    StatsStreamInitConfig               statsStreamInitConfig                 = {};
    StatsCameraInfo                     cameraInfo                            = pInitializeData->cameraInfo;

    // Store the Camera Info
    m_cameraInfo = cameraInfo;

    // In multi camera usecae, get peer pipeline ID
    if ((TRUE == pStaticSettings->multiCameraEnable) && (MultiCamera3ASyncDisabled != pStaticSettings->multiCamera3ASync))
    {
        result = pInitializeData->pPipeline->GetIntraRealtimePipelineId(pInitializeData->pPipeline->GetPipelineId(),
                                                                        &m_peerPipelineId);
    }

    // Read from use case pool to check if we have results from Fast AEC
    statsStreamInitConfig.operationMode = StatsOperationModeNormal;
    StatsUtil::GetStatsStreamInitConfig(
        pInitializeData->pPipeline->GetPerFramePool(PoolType::PerUsecase), &statsStreamInitConfig);

    m_pAWBIOUtil = CAMX_NEW CAWBIOUtil;
    if (NULL != m_pAWBIOUtil)
    {
        result = m_pAWBIOUtil->Initialize(pInitializeData);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupAWB, "IOUtil Create failed - out of memory");
        result = CamxResultENoMemory;
    }

    HwCameraInfo  hwcamerraInfo;

    result = HwEnvironment::GetInstance()->GetCameraInfo(cameraInfo.cameraId, &hwcamerraInfo);

    if (CamxResultSuccess == result)
    {
        m_pAWBIOUtil->FillIlluminantCalibrationFactor(&hwcamerraInfo.pSensorCaps->OTPData.WBCalibration[0],
                                    &illuminantsCalibrationFactor);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupAWB, "failed to get the AWB calibration data");
    }

    statsTuningData.pTuningSetManager =
        reinterpret_cast<VOID*>(pInitializeData->pTuningDataManager->GetChromatix());

    createParams[AWBCreateParamsTypeTuningData].createParamType = AWBCreateParamsTypeTuningData;
    createParams[AWBCreateParamsTypeTuningData].pCreateParam = &statsTuningData;
    createParams[AWBCreateParamsTypeTuningData].sizeOfCreateParam = sizeof(statsTuningData);

    createParams[AWBCreateParamsTypeLoggerFunctionPtr].createParamType = AWBCreateParamsTypeLoggerFunctionPtr;
    createParams[AWBCreateParamsTypeLoggerFunctionPtr].pCreateParam = reinterpret_cast<VOID*>(&StatsLoggerFunction);
    createParams[AWBCreateParamsTypeLoggerFunctionPtr].sizeOfCreateParam = sizeof(StatsLoggingFunction);

    createParams[AWBCreateParamTypeOperationMode].createParamType = AWBCreateParamTypeOperationMode;
    createParams[AWBCreateParamTypeOperationMode].pCreateParam = reinterpret_cast<VOID*>(&statsStreamInitConfig);
    createParams[AWBCreateParamTypeOperationMode].sizeOfCreateParam = sizeof(statsStreamInitConfig);

    ChiStatsSession* pStatsSession                                  = m_pAWBIOUtil->GetChiStatsSessionHandle();
    createParams[AWBCreateParamTypeSessionHandle].createParamType   = AWBCreateParamTypeSessionHandle;
    createParams[AWBCreateParamTypeSessionHandle].pCreateParam      = static_cast<VOID*>(pStatsSession);
    createParams[AWBCreateParamTypeSessionHandle].sizeOfCreateParam = sizeof(ChiStatsSession);

    UINT* pOverrideCameraOpen                                             = (UINT *)&pStaticSettings->overrideCameraOpen;
    createParams[AWBCreateParamTypeCameraOpenIndicator].createParamType   = AWBCreateParamTypeCameraOpenIndicator;
    createParams[AWBCreateParamTypeCameraOpenIndicator].pCreateParam      = static_cast<VOID*>(pOverrideCameraOpen);
    createParams[AWBCreateParamTypeCameraOpenIndicator].sizeOfCreateParam = sizeof(UINT);

    createParams[AWBCreateParamTypeCameraInfo].createParamType   = AWBCreateParamTypeCameraInfo;
    createParams[AWBCreateParamTypeCameraInfo].pCreateParam      = static_cast<VOID*>(&cameraInfo);
    createParams[AWBCreateParamTypeCameraInfo].sizeOfCreateParam = sizeof(StatsCameraInfo);

    createParams[AWBCreateParamsTypeCalibrationData].createParamType   = AWBCreateParamsTypeCalibrationData;
    createParams[AWBCreateParamsTypeCalibrationData].pCreateParam      = static_cast<VOID*>(&illuminantsCalibrationFactor);
    createParams[AWBCreateParamsTypeCalibrationData].sizeOfCreateParam = sizeof(illuminantsCalibrationFactor);

    createParamList.createParamsCount = AWBCreateParamTypeCount;
    createParamList.pCreateParams = &createParams[0];


    if (CamxResultSuccess == result)
    {
        if (NULL != pInitializeData->initializecallback.pAWBCallback)
        {
            m_pfnCreate = pInitializeData->initializecallback.pAWBCallback->pfnCreate;
        }

        if (TRUE == pStaticSettings->disableAWBAlgoCHIOverload)
        {

            if (FALSE == pStaticSettings->enableCustomAlgoAWB)
            {
                pLibraryName = pDefaultAlgorithmAWBLibraryName;
                pLibraryPath = pDefaultAlgorithmAWBLibraryPath;
            }
            else
            {
                pLibraryName = pStaticSettings->customAlgoAWBName;
                pLibraryPath = pStaticSettings->customAlgoAWBPath;
            }

            pFuncCreateAWB = StatsUtil::LoadAlgorithmLib(&m_hHandle, pLibraryPath, pLibraryName, pFunctionName);

            if (NULL == pFuncCreateAWB)
            {
                result = CamxResultEUnableToLoad;
                CAMX_ASSERT_ALWAYS_MESSAGE("Unable to load the algo library! (%s) Path:%s", pLibraryName, pLibraryPath);

                if (NULL != m_hHandle)
                {
                    OsUtils::LibUnmap(m_hHandle);
                    m_hHandle = NULL;
                }
            }
            else
            {
                CREATEAWB pFNCreateAWB = reinterpret_cast<CREATEAWB>(pFuncCreateAWB);
                result = (*pFNCreateAWB)(&createParamList, &m_pAWBAlgorithm);
            }
        }
        else if (NULL != m_pfnCreate)
        {
            result = (*m_pfnCreate)(&createParamList, &m_pAWBAlgorithm);

            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupAWB, "Failed to create algo instance: %d", CamxResultStrings[result]);
            }
        }
    }

    if ((CamxResultSuccess != result) || (NULL == m_pAWBAlgorithm))
    {
        CAMX_LOG_ERROR(CamxLogGroupAWB,
                       "Failed to initialize results: %s, m_pAWBAlgorithm: %p, Create pointer: %p",
                       CamxResultStrings[result],
                       m_pAWBAlgorithm,
                       pStaticSettings->disableAWBAlgoCHIOverload ? pFuncCreateAWB : reinterpret_cast<VOID*>(m_pfnCreate));

        result = CamxResultENoMemory;
    }

    if (CamxResultSuccess == result)
    {
        result = VendorTagListAllocation();
    }

    if (CamxResultSuccess == result)
    {
        result = SetOperationModetoAlgo(pInitializeData->statsStreamInitConfig.operationMode);
    }

    if (CamxResultSuccess == result)
    {
        result = PublishPreRequestOutput();
        if (result != CamxResultSuccess)
        {
            CAMX_LOG_ERROR(CamxLogGroupAWB, "Failed to publish pre request data result: %s", CamxResultStrings[result]);
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::ExecuteProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::ExecuteProcessRequest(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo)
{
    CAMX_ASSERT(NULL != pStatsProcessRequestDataInfo);
    CAMX_ENTRYEXIT_SCOPE_ID(CamxLogGroupAWB, SCOPEEventCAWBStatsProcessorExecuteProcessRequest,
        pStatsProcessRequestDataInfo->requestId);

    CamxResult              result              = CamxResultSuccess;
    const StaticSettings*   pStaticSettings     = HwEnvironment::GetInstance()->GetStaticSettings();
    AWBAlgoInputList        algoInputList       = { 0 };
    AWBAlgoOutputList       algoOutputList      = { 0 };
    AWBAlgoSetParamList     algoSetParamList    = { 0 };
    AWBAlgoGetParam         algoGetParam        = {};
    AWBAlgoOutputList*      pFinalOutputList    = &algoOutputList;

    m_pAWBIOUtil->InvalidateIO();

    BOOL isAECSettled = m_pAWBIOUtil->IsAECSettled();

    if (TRUE == pStatsProcessRequestDataInfo->skipProcessing)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAWB,
                         "Skipping AWB Algo processing for RequestId=%llu",
                         pStatsProcessRequestDataInfo->requestId);

        m_pAWBIOUtil->PublishSkippedFrameOutput();
    }
    else if (TRUE == pStaticSettings->disableAWBStatsProcessing)
    {
        // If AWB stats processing is disabled we publish some hard coded values to the metadata pool
        result = OverwriteAWBOutput(pStatsProcessRequestDataInfo, &algoOutputList);
    }
    else
    {

        BOOL parseStats = (pStaticSettings->maxPipelineDelay < pStatsProcessRequestDataInfo->requestId) ? TRUE : FALSE;

        if (((StatsOperationModeFastConvergence == pStatsProcessRequestDataInfo->operationMode) &&
            (FALSE == isAECSettled)))
        {
            CAMX_LOG_VERBOSE(CamxLogGroupAWB, "Skip AWB processing. ReqId %llu OpMode: %d AEC Settled %d",
                pStatsProcessRequestDataInfo->requestId,
                pStatsProcessRequestDataInfo->operationMode,
                isAECSettled);
            parseStats = FALSE;
        }

        CAMX_ASSERT(NULL != m_pAWBAlgorithm);

        if (CamxResultSuccess == result)
        {
            result = m_pAWBIOUtil->GetAlgoSetParamInput(pStatsProcessRequestDataInfo, &algoSetParamList);

            if ((TRUE == pStaticSettings->multiCameraEnable) &&
                (MultiCamera3ASyncDisabled != pStaticSettings->multiCamera3ASync))
            {
                result = m_pAWBIOUtil->SetPeerInfo(pStatsProcessRequestDataInfo, &algoSetParamList, m_peerPipelineId);
            }
        }

        if (CamxResultSuccess == result)
        {
            result = m_pAWBAlgorithm->AWBSetParam(m_pAWBAlgorithm, &algoSetParamList);
        }

        if (CamxResultSuccess == result)
        {
            result = m_pAWBIOUtil->GetAlgoProcessInput(pStatsProcessRequestDataInfo, &algoInputList, parseStats);
            if (CamxResultSuccess != result)
            {
                parseStats = FALSE;
                result = CamxResultSuccess;
            }
        }

        m_pAWBIOUtil->GetAlgoExpectedOutputList(pStatsProcessRequestDataInfo, &algoOutputList);

        // Process stats
        if (CamxResultSuccess == result)
        {
            result = m_pAWBAlgorithm->AWBProcess(m_pAWBAlgorithm, &algoInputList, &algoOutputList);
            if ((CamxResultSuccess == result) && (FALSE == parseStats))
            {
                if (CamxResultSuccess != IsValidAlgoOutput(&algoOutputList, pStatsProcessRequestDataInfo->requestId))
                {
                    algoGetParam.type = AWBGetParamTypeLastOutput;
                    result = AlgoGetParam(pStatsProcessRequestDataInfo, &algoGetParam);
                    pFinalOutputList = static_cast<AWBAlgoOutputList*>(
                        algoGetParam.outputInfoList.pGetParamOutputs[AWBGetParamOutputTypeOutputList].pGetParamOutput);
                }
                else
                {
                    CAMX_LOG_VERBOSE(CamxLogGroupStats,
                        "Valid AWB Algo output for RequestId=%llu",
                        pStatsProcessRequestDataInfo->requestId);
                }
            }


            // Publish cross pipeline property for multi camera
            m_pAWBIOUtil->PublishCrossProperty(pStatsProcessRequestDataInfo);
        }

        // query for Flash estimation state
        if (CamxResultSuccess == result)
        {
            algoGetParam.type = AWBGetParamTypeFlashEstimationState;
            result = AlgoGetParam(pStatsProcessRequestDataInfo, &algoGetParam);
        }

        if (CamxResultSuccess == result)
        {
            AWBAlgoFlashEstimationProgress* pFlashEstimationState = static_cast<AWBAlgoFlashEstimationProgress*>(
                algoGetParam.outputInfoList.pGetParamOutputs[AWBGetParamOutputTypeFlashEstimationProgress].pGetParamOutput);
            if ((NULL != pFlashEstimationState) && (AWBAlgoFlashEstimationDone == *pFlashEstimationState))
            {
                // Preflash complete. Retrieve Flash Gain.
                algoGetParam.type = AWBGetParamTypeFlashOutput;
                result = AlgoGetParam(pStatsProcessRequestDataInfo, &algoGetParam);
            }
        }

        if (CamxResultSuccess == result)
        {
            result = m_pAWBIOUtil->PublishMetadata(pStatsProcessRequestDataInfo,
                                                   pFinalOutputList,
                                                   &algoGetParam.outputInfoList);
        }
    }

    if ((CamxResultSuccess == result) && (TRUE == pStaticSettings->multiCameraEnable) &&
        (MultiCamera3ASyncDisabled != pStaticSettings->multiCamera3ASync))
    {
        algoGetParam.type = AWBGetParamTypePeerInfo;
        result = AlgoGetParam(pStatsProcessRequestDataInfo, &algoGetParam);

        if (CamxResultSuccess == result)
        {
            VOID* pPeerInfo = algoGetParam.outputInfoList.pGetParamOutputs[AWBGetParamOutputTypePeerInfo].pGetParamOutput;
            result = m_pAWBIOUtil->PublishPeerInfo(pStatsProcessRequestDataInfo, pPeerInfo);
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::AddOEMDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::AddOEMDependencies(
    const StatsProcessRequestData*  pStatsProcessRequestDataInfo,
    StatsDependency*                pStatsDependency)
{
    CamxResult              result             = CamxResultSuccess;
    AWBAlgoGetParam         algoGetParam       = {};
    StatsVendorTagInfoList* pVendorTagInfoList = NULL;

    // Get list of dependent vendor tags from algo
    algoGetParam.type     = AWBGetParamTypeDependentVendorTags;

    result = m_pAWBIOUtil->GetAlgoGetParamInputOutput(pStatsProcessRequestDataInfo, &algoGetParam);
    if (CamxResultSuccess == result)
    {
        result = m_pAWBAlgorithm->AWBGetParam(m_pAWBAlgorithm, &algoGetParam);
    }

    if (CamxResultSuccess == result)
    {
        AWBAlgoGetParamOutputList*  pGetParamOutput = &algoGetParam.outputInfoList;
        for (UINT32 i = 0; i < pGetParamOutput->getParamOutputCount; i++)
        {
            if ((AWBGetParamOutputTypeDependentVendorTags == pGetParamOutput->pGetParamOutputs[i].getParamOutputType) &&
                (0 != pGetParamOutput->pGetParamOutputs[i].sizeOfWrittenGetParamOutput))
            {
                pVendorTagInfoList =
                    static_cast<StatsVendorTagInfoList*>(algoGetParam.outputInfoList.pGetParamOutputs[i].pGetParamOutput);
                break;
            }
        }
    }

    // Find and add unpublished vendor tags to the dependency list
    // Note that some algo may not have any dependent vendor tags
    if ((CamxResultSuccess == result) && (NULL != pVendorTagInfoList))
    {
        UINT32  propertyCount = Utils::MinUINT32(pVendorTagInfoList->vendorTagCount,
                                                    (MaxStatsProperties - pStatsDependency->propertyCount));

        // Adding vendor tag dependencies
        for (UINT32 i = 0; i < propertyCount; i++)
        {
            pStatsDependency->properties[pStatsDependency->propertyCount + i].property =
                pVendorTagInfoList->vendorTag[i].vendorTagId;

            CAMX_LOG_VERBOSE(CamxLogGroupAWB,
                                "Added Vendor Tag(%u) to the dependent list. requestID(%llu)",
                                pVendorTagInfoList->vendorTag[i].vendorTagId,
                                pStatsProcessRequestDataInfo->requestId);
        }

        pStatsDependency->propertyCount += propertyCount;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::GetDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::GetDependencies(
    const StatsProcessRequestData*  pStatsProcessRequestDataInfo,
    StatsDependency*                pStatsDependency)
{
    CAMX_ENTRYEXIT(CamxLogGroupAWB);

    CamxResult result = CamxResultSuccess;

    if (TRUE == pStatsProcessRequestDataInfo->skipProcessing)
    {
        // We need to publish previous frame data to property pool when skipProcessing flag is set.
        // Add previous frame(offset = 1) AWBS Frame/Stats Control properties as dependency.
        pStatsDependency->properties[0] = { PropertyIDAWBFrameControl, 1,  StatsDependencyGroup::GROUP_NORMAL };
        pStatsDependency->properties[1] = { PropertyIDAWBStatsControl, 1,  StatsDependencyGroup::GROUP_NORMAL };

        pStatsDependency->propertyCount = 2;
    }
    else
    {
        // Currently AWB doesn't depend on any properties other than AEC.
        // Since AEC always runs before AWB, we don't need to add AEC in dependent properties list.
        // Add only OEM vendor tag dependencies
        result = AddOEMDependencies(pStatsProcessRequestDataInfo, pStatsDependency);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::IsDependenciesSatisfied
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::IsDependenciesSatisfied(
    const StatsProcessRequestData*  pStatsProcessRequestDataInfo,
    BOOL*                           pIsSatisfied)
{
    CAMX_ENTRYEXIT(CamxLogGroupAWB);

    CamxResult      result = CamxResultSuccess;
    BOOL            isStatisfied = FALSE;
    StatsDependency statsDependency = { 0 };

    /// @todo  (CAMX-523): query algorithm to check if dependencies are satisfied.

    // Get stats property dependency list
    result = GetDependencies(pStatsProcessRequestDataInfo, &statsDependency);

    isStatisfied = (statsDependency.propertyCount == 0) ? TRUE : FALSE;

    *pIsSatisfied &= isStatisfied;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::IsValidAlgoOutput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::IsValidAlgoOutput(
    AWBAlgoOutputList* pAlgoOutputList,
    UINT64             requestId)
{
    CamxResult    result = CamxResultSuccess;
    AWBAlgoGains gains = { 0 };

    // Check if we received all the outputs.
    // Note that currently algo can skip outputting BG config data. Subnode needs fill the BG config data in such case.
    for (UINT32 i = 0; i < pAlgoOutputList->outputCount; i++)
    {
        if (pAlgoOutputList->pAWBOutputs[i].outputType == AWBOutputTypeGains)
        {
            gains = *reinterpret_cast<AWBAlgoGains*>(pAlgoOutputList->pAWBOutputs[i].pAWBOutput);
            CAMX_LOG_DEBUG(CamxLogGroupStats, "gains from algo for ReqId :%llu Red  = %f, green  = %f, blue  = %f",
                requestId, gains.red, gains.green, gains.blue);
            if ((0.0f == gains.red) || (0.0f == gains.green) || (0.0f == gains.blue))
            {
                result = CamxResultEFailed;
            }
            break;
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::PublishPreRequestOutput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::PublishPreRequestOutput()
{
    CamxResult            result          = CamxResultSuccess;
    const StaticSettings* pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();

    if (TRUE == pStaticSettings->disableAWBStatsProcessing)
    {
        AWBAlgoOutputList algoOutputList = { 0 };

        result = OverwriteAWBOutput(NULL, &algoOutputList);
    }
    else
    {
        AWBAlgoGetParam getParam = {};

        getParam.type = AWBGetParamTypeLastOutput;
        result = AlgoGetParam(NULL, &getParam);

        // query for BG Configuration
        if (CamxResultSuccess == result)
        {
            getParam.type = AWBGetParamTypeBGConfig;
            result = AlgoGetParam(NULL, &getParam);
        }
    }

    m_pAWBIOUtil->PrePublishMetadata();

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::AlgoGetParam
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::AlgoGetParam(
    const StatsProcessRequestData*  pStatsProcessRequestDataInfo,
    AWBAlgoGetParam*                pGetParam)
{
    CamxResult result = CamxResultSuccess;

    result = m_pAWBIOUtil->GetAlgoGetParamInputOutput(pStatsProcessRequestDataInfo, pGetParam);

    // Get initial data
    if (CamxResultSuccess == result)
    {
        result = m_pAWBAlgorithm->AWBGetParam(m_pAWBAlgorithm, pGetParam);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::SetOperationModetoAlgo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::SetOperationModetoAlgo(
    StatsOperationMode opMode)
{
    CamxResult          result                                   = CamxResultSuccess;
    const UINT32        operationModeSetParamCount               = 2;
    AWBAlgoSetParamList algoParamList                            = { 0 };
    AWBAlgoSetParam     algoSetParam[operationModeSetParamCount] = {};

    if (StatsOperationModeInvalid == opMode)
    {
        CAMX_LOG_ERROR(CamxLogGroupAWB, "Invalid opMode Set: %d", opMode);
        result = CamxResultEInvalidArg;
    }

    AWBAlgoOperationModeType opModeAWB;

    switch (opMode)
    {
        case StatsOperationModeFastConvergence:
            opModeAWB = AWBAlgoOperationModeFastConvergence;
            break;
        case StatsOperationModeNormal:
            opModeAWB = AWBAlgoOperationModeStreaming;
            break;
        default:
            break;
    }

    if (result == CamxResultSuccess)
    {
        algoSetParam[0].setParamType     = AWBSetParamTypeOperationMode;
        algoSetParam[0].sizeOfInputParam = sizeof(AWBAlgoOperationModeType);
        algoSetParam[0].pAWBSetParam     = static_cast<VOID*>(&opModeAWB);
        // Besides AWB operation mode, we still need to pass camera information as well
        algoSetParam[1].setParamType     = AWBSetParamTypeCameraInfo;
        algoSetParam[1].sizeOfInputParam = sizeof(StatsCameraInfo);
        algoSetParam[1].pAWBSetParam     = static_cast<VOID*>(&m_cameraInfo);

        algoParamList.inputParamsCount   = operationModeSetParamCount;
        algoParamList.pAWBSetParams      = algoSetParam;
        CAMX_LOG_DEBUG(CamxLogGroupAWB, "Mode set to Algo: %d", opModeAWB);
        result = m_pAWBAlgorithm->AWBSetParam(m_pAWBAlgorithm, &algoParamList);
    }

    if (CamxResultEUnsupported == result)
    {
        result = CamxResultSuccess;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::OverwriteAWBOutput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAWBStatsProcessor::OverwriteAWBOutput(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo,
    AWBAlgoOutputList*             pOutputList)
{
    CamxResult              result          = CamxResultSuccess;
    const StaticSettings*   pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();
    AWBAlgoGains*           pAWBGain        = NULL;

    CAMX_ASSERT(NULL != pStaticSettings);

    m_pAWBIOUtil->GetAlgoExpectedOutputList(pStatsProcessRequestDataInfo, pOutputList);
    for (UINT32 i = 0; i < pOutputList->outputCount; i++)
    {
        switch (pOutputList->pAWBOutputs[i].outputType)
        {
            case AWBOutputTypeGains:
                pAWBGain           = static_cast<AWBAlgoGains*>(pOutputList->pAWBOutputs[i].pAWBOutput);
                pAWBGain->red      = pStaticSettings->rGain;
                pAWBGain->green    = pStaticSettings->gGain;
                pAWBGain->blue     = pStaticSettings->BGain;
                break;
            case AWBOutputTypeColorTemperature:
                *static_cast<UINT32*>(pOutputList->pAWBOutputs[i].pAWBOutput) = pStaticSettings->colorTemp;
                break;
            case AWBOutputTypeIlluminantType:
                *static_cast<StatsIlluminantType*>(pOutputList->pAWBOutputs[i].pAWBOutput) = StatsIlluminantTL84;
                break;
            case AWBOutputTypeSampleDecision:
                break;
            case AWBOutputTypeBGConfig:
                m_pAWBIOUtil->GetDefaultBGConfig(
                    static_cast<StatsBayerGridBayerExposureConfig*>(pOutputList->pAWBOutputs[i].pAWBOutput));
                break;
            case AWBOutputTypeState:
                *static_cast<AWBAlgoState*>(pOutputList->pAWBOutputs[i].pAWBOutput) = AWBAlgoStateConverged;
                break;
            case AWBOutputTypeMode:
                *static_cast<AWBAlgoMode*>(pOutputList->pAWBOutputs[i].pAWBOutput) = AWBAlgoModeAuto;
                break;
            case AWBOutputTypeLock:
                *static_cast<BOOL*>(pOutputList->pAWBOutputs[i].pAWBOutput) = FALSE;
                break;
            case AWBOutputTypeVendorTag:
                break;
            default:
                break;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAWBStatsProcessor::~CAWBStatsProcessor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAWBStatsProcessor::~CAWBStatsProcessor()
{
    // Destroy all the created objects.
    CAMX_LOG_INFO(CamxLogGroupAWB, "CAWBStatsProcessor::Destroy");
    const StaticSettings* pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();
    CAMX_ASSERT_MESSAGE(NULL != pStaticSettings, "pStaticSettings NULL pointer");

    StatsCameraInfo cameraInfo = m_cameraInfo;

    if (NULL != m_pAWBIOUtil)
    {
        CAMX_DELETE m_pAWBIOUtil;
        m_pAWBIOUtil = NULL;
    }

    if (NULL != m_pAWBAlgorithm)
    {
        AWBAlgoDestroyParamList destroyParamList                        = { 0 };
        AWBAlgoDestroyParam     destroyParams[AWBDestroyParamTypeCount] = {};
        UINT                    overrideCameraClose                     = pStaticSettings->overrideCameraClose;

        destroyParams[AWBDestroyParamTypeCameraCloseIndicator].destroyParamType = AWBDestroyParamTypeCameraCloseIndicator;
        destroyParams[AWBDestroyParamTypeCameraCloseIndicator].pParam           = &overrideCameraClose;
        destroyParams[AWBDestroyParamTypeCameraCloseIndicator].sizeOfParam      = sizeof(UINT);

        destroyParams[AWBDestroyParamTypeCameraInfo].destroyParamType = AWBDestroyParamTypeCameraInfo;
        destroyParams[AWBDestroyParamTypeCameraInfo].pParam = static_cast<VOID*>(&cameraInfo);
        destroyParams[AWBDestroyParamTypeCameraInfo].sizeOfParam = sizeof(cameraInfo);

        destroyParamList.paramCount = AWBDestroyParamTypeCount;
        destroyParamList.pParamList = &destroyParams[0];

        m_pAWBAlgorithm->AWBDestroy(m_pAWBAlgorithm, &destroyParamList);
        m_pAWBAlgorithm = NULL;
    }

    if (NULL != m_hHandle)
    {
        OsUtils::LibUnmap(m_hHandle);
        m_hHandle = NULL;
    }
}

CAMX_NAMESPACE_END
